/**
 * 
 */
package org.xnat.xnatfs.webdav;

import java.io.File;
import java.net.URL;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.xnat.xnatfs.asset.CachingAssetProvider;
import org.xnat.xnatfs.connection.ConnectionFactory;
import org.xnat.xnatfs.connection.HttpConnection;

import com.bradmcevoy.http.MiltonServlet;
import com.martiansoftware.jsap.FlaggedOption;
import com.martiansoftware.jsap.JSAP;
import com.martiansoftware.jsap.JSAPResult;
import com.martiansoftware.jsap.SimpleJSAP;
import com.martiansoftware.jsap.Switch;
import com.martiansoftware.jsap.UnflaggedOption;
import com.martiansoftware.jsap.defaultsources.PropertyDefaultSource;

/**
 * @author blezek
 * 
 */
public class StartJettyServer {
  public enum OSType {
    WINDOWS, MACINTOSH, UNIX
  }

  static Logger logger = Logger.getLogger ( StartJettyServer.class );

  public static void main ( String[] args ) {
    HttpConnection connection = new HttpConnection ();
    ConnectionFactory.setConnection ( connection );

    configureLogging ();
    JSAPResult config = parseArguments ( args );

    connection.setURL ( config.getString ( "url" ) );

    Server server = new Server ( config.getInt ( "serverport" ) );

    ServletContextHandler context = new ServletContextHandler ( ServletContextHandler.SESSIONS );
    context.setContextPath ( "/" );
    server.setHandler ( context );
    // ServletHolder holder = new ServletHolder ( new XnatFSServlet () );
    ServletHolder holder = new ServletHolder ( new MiltonServlet () );

    context.addServlet ( holder, "/*" );

    if ( config.getBoolean ( "cache" ) ) {
      holder.setInitParameter ( XNATFS.XNATFS_ASSETPROVIDER_CLASS, CachingAssetProvider.class.getName () );
    }
    holder.setInitParameter ( "resource.factory.class", "org.xnat.xnatfs.webdav.XNATFS" );
    holder.setInitParameter ( "filter_0", "org.xnat.xnatfs.filter.AuthenticationFilter" );
    holder.setInitParameter ( "filter_1", "org.xnat.xnatfs.filter.ChatterFilter" );

    try {
      server.start ();
    } catch ( Exception e ) {
      logger.error ( "Error starting Jetty server: ", e );
      e.printStackTrace ();
    }
  }

  public static void configureLogging () {
    File props;
    props = new File ( System.getProperty ( "user.dir", "." ), "log4j.properties" );
    if ( props.exists () && props.canRead () ) {
      System.out.println ( "Found props file: " + props.getAbsoluteFile () );
      PropertyConfigurator.configure ( props.getAbsolutePath () );
    } else {
      props = new File ( getApplicationResourceDirectory ( "xnatfs" ), "log4j.properties" );
      if ( props.exists () && props.canRead () ) {
        System.out.println ( "Found props file: " + props.getAbsoluteFile () );
        PropertyConfigurator.configure ( props.getAbsolutePath () );
      } else {
        URL url = XNATFS.class.getResource ( "/log4j.properties" );
        if ( url != null ) {
          System.out.println ( "Found props url: " + url );
          PropertyConfigurator.configure ( url );
        } else {
          System.out.println ( "No props file found falling back to defaults" );
          BasicConfigurator.configure ();
        }
      }
    }
  }

  /**
   * Returns the appropriate working directory for storing application data. The result of this method is platform dependant: On linux, it will return ~/applicationName, on windows, the working directory will be located in the user's application data folder. For Mac OS systems, the working directory will be placed in the proper location in "Library/Application Support".
   * <p/>
   * This method will also make sure that the working directory exists. When invoked, the directory and all required subfolders will be created.
   * 
   * @param applicationName
   *          Name of the application, used to determine the working directory.
   * @return the appropriate working directory for storing application data.
   */
  public static File getApplicationResourceDirectory ( final String applicationName ) {
    final String userHome = System.getProperty ( "user.home", "." );
    final File workingDirectory;
    switch ( getOS () ) {
    case UNIX:
      workingDirectory = new File ( userHome, '.' + applicationName + '/' );
      break;
    case WINDOWS:
      final String applicationData = System.getenv ( "APPDATA" );
      if ( applicationData != null )
        workingDirectory = new File ( applicationData, "." + applicationName + '/' );
      else
        workingDirectory = new File ( userHome, '.' + applicationName + '/' );
      break;
    case MACINTOSH:
      workingDirectory = new File ( userHome, "Library/Application Support/" + applicationName );
      break;
    default:
      return new File ( "." );
    }
    if ( !workingDirectory.exists () && !workingDirectory.mkdirs () ) {
      throw new RuntimeException ( "The working directory could not be created: " + workingDirectory );

    }
    return workingDirectory;
  }

  static OSType getOS () {
    String sysName = System.getProperty ( "os.name" ).toLowerCase ();
    if ( sysName.contains ( "windows" ) ) {
      return OSType.WINDOWS;
    } else if ( sysName.contains ( "mac" ) ) {
      return OSType.MACINTOSH;
    } else {
      return OSType.UNIX;
    }
  }

  @SuppressWarnings("unchecked")
  static JSAPResult parseArguments ( String[] args ) {
    JSAPResult config = null;
    try {
      SimpleJSAP jsap = new SimpleJSAP ( "xnatfs", "xnatfs is a WebDAV server.  xnatfs allows "
          + " local mounting of an XNAT instance.  Users of the system can browse projects, experiments, subjects and data using the " + " a WebDAV filesystem, rather than the web interface." );

      FlaggedOption url = new FlaggedOption ( "url" ).setStringParser ( JSAP.STRING_PARSER ).setRequired ( true ).setShortFlag ( 'u' ).setLongFlag ( "url" );
      jsap.registerParameter ( url );
      url.setHelp ( "URL for XNAT server.  Should be in the form of a web site address, i.e. http://central.xnat.org/REST.  "
          + "The URL should end with '/REST' so xnatfs can properly find the REST api of the XNAT server." );

      Switch cache = new Switch ( "cache" ).setLongFlag ( "cache" ).setDefault ( "true" );
      jsap.registerParameter ( cache );
      cache.setHelp ( "Cache the communications with the XNAT REST API (can provide improved speed, at the cost of increased memory usage and possible synchronization delays)." );

      // Get a list of remaining options
      UnflaggedOption serverPort = new UnflaggedOption ( "serverport" ).setStringParser ( JSAP.INTEGER_PARSER ).setDefault ( "8081" ).setRequired ( false ).setGreedy ( false );
      serverPort.setHelp ( "Port number to run the local server on, default is 8081" );
      jsap.registerParameter ( serverPort );

      // Register the default sources. First in the application directory, next
      // in the local directory
      File defaultsFile = new File ( getApplicationResourceDirectory ( "xnatfs" ), "xnatfs.props" );
      jsap.registerDefaultSource ( new PropertyDefaultSource ( defaultsFile.getAbsolutePath (), false ) );
      defaultsFile = new File ( System.getProperty ( "user.dir", "." ), "xnatfs.props" );
      jsap.registerDefaultSource ( new PropertyDefaultSource ( defaultsFile.getAbsolutePath (), false ) );

      config = jsap.parse ( args );

      if ( !config.success () ) {
        System.err.println ();
        // print out specific error messages describing the problems
        // with the command line, THEN print usage, THEN print full
        // help. This is called "beating the user with a clue stick."
        System.err.println ( "The following errors were found while parsing the command line:" );
        for ( java.util.Iterator errs = config.getErrorMessageIterator (); errs.hasNext (); ) {
          System.err.println ( "Error: " + errs.next () );
        }

        System.err.println ();
        System.err.println ( "Usage: java " + StartJettyServer.class.getName () );
        System.err.println ( "                " + jsap.getUsage () );
        System.err.println ();
        System.err.println ( jsap.getHelp () );
        System.exit ( 1 );
      }
    } catch ( Exception e ) {
      System.err.println ( "parsing command line " + e );
      System.exit ( 1 );
    }
    if ( config.userSpecified ( "help" ) ) {
      System.exit ( 0 );
    }
    return config;
  }

}
